# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# Author: Michael DiBernardo (mikedebo@gmail.com)
# Copyright Michael DiBernardo 2009

"""
A 'ticker' is a named counter, mapped to an url, that keeps track of
the information that is sent to it via an HTTP get. The parameters of a call to
a ticker are sent as part of the url. The url is of the format
/<tickertype>/<ticker instance name>/<identity>/<extra>/<params>/<after>.
"Identity" is some token that is supposed to identify the caller.

There are three types of tickers:

A PingTicker simply keeps track of each time that someone visits
its url. For instance, if you create a ticker named 'pingme', visits to
/ping/identity/pingme/ a tick will be registered for the time of the event.

A CounterTicker additionally stores a numerical value with its tick, so that
these values can be aggregated / analyzed later. For instance, if you create a
ticker named 'countme', visits to /counter/identity/counter/<value> will store
a tick with value 'value'.

A DurationTicker registers start/stop events. For every start event, a normal
Tick is used. For every end event, an EndTick is used. The EndTick stores the
computed duration of time between the start and the end event. 

Thus, if you order the ticks on a DurationTicker by timestamp FOR A SINGLE
IDENTITY, it should be an alternating sequence of Ticks and EndTicks, starting
with a Tick.

Note that it is assumed that each identity is unique, and sending such an
alternating sequence. If the same identity sends two start events or two end
events in a row, or begins with an end event, it is considered an error.
"""
from django.db import models
import tikk.thirdparty.timedeltafield as timedeltafield

TICKERS = (
    ('ping', 'Ping'),
    ('counter', 'Counter'),
    ('duration', 'Duration'),
)

class Ticker(models.Model):
    """
    A PingTicker simply keeps track of the number of times that someone visits
    its url. For instance, if you create a ticker named 'pingme', visits to
    /ping/identity/pingme/ will increment the value of the PingTicker each time.
    """
    name = models.CharField(max_length=128)
    type = models.CharField(max_length=10, choices=TICKERS)

class Tick(models.Model):
    """
    A Tick is a single event logged against a ticker. These are the types of
    ticks that are used by Ping tickers, and are also used as the base tick
    implementation for all other tickers.

    """
    # The ticker this tick is registered with.
    ticker = models.ForeignKey(Ticker)
    # The time the tick was registered.
    timestamp = models.DateTimeField(auto_now=False, auto_now_add=False)
    # Some token that represents the entity that registered this tick.
    identity = models.CharField(max_length=128)
    
    class Meta:
        ordering = ['-timestamp', 'identity']

class CounterTick(Tick):
    """
    A tick that stores a single numerical value in addition to all other
    tick-ish information.
    """
    # The value that was sent with this tick.
    value = models.FloatField()

class EndTick(Tick):
    """
    A Tick that represents the end of some duration. The start of this duration
    is marked by a vanilla Tick object.
    """
    # The duration between this and the corresponding start tick.
    duration = timedeltafield.TimedeltaField()
    
    @staticmethod
    def createFromStartTick(start_tick, timestamp):
        """
        Create a end tick for the given start_tick, using the timestamp given.
        This does NOT save the created end tick.
        """
        new_tick = EndTick()
        new_tick.ticker = start_tick.ticker
        new_tick.timestamp = timestamp
        new_tick.identity = start_tick.identity
        
        # Compute the duration of time that has passed.
        new_tick.duration = timestamp - start_tick.timestamp
        return new_tick

def get_all_ticks_for(ticker, id=None):
    """
    Get the ticks registered with the ticker (or ticker of the given name).
    This will raise an error if a ticker with that name does not exist, or if
    there aren't any ticks registered with that ticker.

    You can optionally supply an id to get ticks registered for that identity.
    """
    if (type(ticker) is str):
        ticker_obj = Ticker.objects.filter(name__exact=ticker).get()
        return get_all_ticks_for(ticker_obj, id)

    tick_set = None
    if (id):
        tick_set = ticker.tick_set.filter(identity__exact=id)
    else:
        tick_set = ticker.tick_set.all()

    return tick_set

def get_most_recent_tick_for(ticker, id=None):
    """
    Get the most recent tick registered with the ticker (or ticker of the given
    name). This will raise an error if a ticker with that name does not exist,
    or if there aren't any ticks registered with that ticker.

    You can optionally supply an id to get the most recent tick registered for
    that identity.
    """
    tick_set = get_all_ticks_for(ticker, id)
    return tick_set.order_by("-timestamp")[0]
